Skip to content

+ add full NuFX ShrinkIt archive read-write support - #128

Merged
Hawkynt merged 1 commit into
mainfrom
feat/nufx-shrinkit-archive
Aug 30, 2026
Merged

+ add full NuFX ShrinkIt archive read-write support#128
Hawkynt merged 1 commit into
mainfrom
feat/nufx-shrinkit-archive

Conversation

@Hawkynt

@Hawkynt Hawkynt commented Aug 29, 2026

Copy link
Copy Markdown
Owner

What changed

Adds genuine NuFX / ShrinkIt archive support for Apple II and Apple IIgs archives. This PR contains the corrected NuLZW commit from #126 plus one NuFX container commit; after #126 merges, the diff naturally reduces to the NuFX-only commit.

Read / validation

  • parses the 48-byte master header and v0/v1/v2/v3 record/thread layouts
  • validates master CRC, record-header CRC and v3 uncompressed thread CRC-16/XMODEM
  • supports filename threads and deprecated in-header filenames, Mac Roman names and filesystem separators
  • extracts Stored, native headerless Squeeze, NuLZW/1 and NuLZW/2 file/disk threads
  • recognizes LZC-12/LZC-16 structurally without falsely claiming a decoder
  • reads plain SHK/SDK and locates Binary-II-wrapped BXY NuFX data; wrapped archives are intentionally read-only for direct mutation
  • header/structure/integrity validator support

Squeeze interoperability

NuFX format-1 threads are not standalone .SQ files. They omit the 76 FF/checksum/filename header and begin directly with the node table. This PR therefore includes a dedicated NuFX Squeeze path with:

  • historical 0x90 RLE (value, 0x90, count; delimiter escaped as 0x90,0)
  • serialized signed-child Huffman node table rooted at node 0
  • LSB-first Huffman codes and EOF symbol 256
  • zero-node empty-stream representation
  • guard-byte behavior compatible with read-ahead decoders
  • a byte-level regression test that fails if a NuFX thread accidentally contains the standalone 76 FF header

Creation

  • normal multi-record .shk
  • single disk-image .sdk with 512-byte block-count/storage semantics
  • Stored, Squeeze, NuLZW/1, NuLZW/2 and Auto-smallest methods
  • version-3 CRCs and canonical filename threads with ShrinkIt-style reserve
  • ProDOS file type, aux type and access options
  • rejects password/encryption and unsafe paths

Genuine direct R/W

CanModify is backed by byte-level record editing:

  • append inserts a complete record at current master EOF
  • replace rewrites only the selected record extent and shifts the following raw tail
  • remove closes only the record extent and clears the truncated physical tail
  • every individual edit immediately patches pre-edit record_count ± n, exact master EOF and master CRC, so a following edit never reparses stale metadata
  • replace preserves filename buffers, resource forks, comments, option lists and unknown threads byte-for-byte; only the selected data thread is re-encoded
  • records missing a data fork can gain one without discarding their existing threads
  • rare v2 direct replacement is refused because its thread CRC semantics differ; safe raw append/remove remain available

Maintenance

  • defrag compacts documented oversized filename/comment reserves while copying data/unknown threads raw
  • shrink uses the same content-preserving compaction and only emits it when actually smaller
  • archive layout map exposes master + real record extents

Tests

All writable methods, nested/Mac-Roman names, SDK, Squeeze wire representation/RLE edges, sequential multi-add master bookkeeping, byte-preserving tail replacement, removal, shrink/defrag payload preservation, CRC validation, encryption rejection and unsafe paths.

Depends on #126 for NuLzwCodec.

@Hawkynt
Hawkynt changed the base branch from feat/shrinkit-nulzw-building-block to main August 29, 2026 11:14
@Hawkynt
Hawkynt force-pushed the feat/nufx-shrinkit-archive branch 3 times, most recently from 7f49e48 to 81c027a Compare August 29, 2026 11:21
@Hawkynt
Hawkynt force-pushed the feat/nufx-shrinkit-archive branch 2 times, most recently from 31aab24 to 28e35bb Compare August 30, 2026 01:02
Records, their threads, and the LZW/1 and LZW/2 dialects ShrinkIt stores them
in, with the CRC each thread carries checked on the way back out.

Two things stopped it building: the encoder held its running LZW prefix in a
byte, which cannot hold a code past 255, and a record's 16-bit attribute
count was bounded against a megabyte, which it can never reach.
@Hawkynt
Hawkynt force-pushed the feat/nufx-shrinkit-archive branch from 28e35bb to 894a91d Compare August 30, 2026 01:13
@Hawkynt
Hawkynt merged commit 13fc8bb into main Aug 30, 2026
3 checks passed
@Hawkynt
Hawkynt deleted the feat/nufx-shrinkit-archive branch August 30, 2026 03:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant